/* includes */
#include "float.h"
#include "utils/stringutils.h"
#include "utils/vectorutils.h"
#include "defines.h"
#include <vector>
#include <sstream>
#include <cassert>
#include <iostream>
#include <cstdlib>


using namespace std;

// ES NECESARIO DECLARAR LAS VARIABLES STATIC DE UNA CLASE COMO SI FUERAN GLOBALES
// SI SE LAS DESEA USAR
int Float::limit;

/** contructor por defecto (es un 0)
 */
Float::Float() {
    mantisa = vector<byte>(0,0);
    exponente = 0;
    signo = false;
}

/** funcion para inicializar el Float a partir de un string*/
void Float::init(const string & num)
{
    string ent = "";
    string frac = "";
    //calcula exponente y singno y borra los ceros redundantes
    signo = StringUtils::signoYSeparar(num, ent, frac);
    bool hayEntero = (ent.size() != 0 && StringUtils::noCero(ent));
    bool hayFraccion = (frac.size() != 0 && StringUtils::noCero(frac));
    //converitr num a binario
    // lo deja en el siguiente formato.
    // string[0] es el mas significativo.
    if(hayEntero)
        StringUtils::entChar2bin(ent);

    if(hayFraccion)
        StringUtils::fracChar2bin(frac, getLimit());

    exponente = StringUtils::calcularPotencia(ent, frac);
    string numB2 = ent + frac;
    int rest = (numB2.size() % 8 == 0) ? 0 : 1;
    mantisa = vector<byte>(numB2.size() / 8 + rest,0);
    byte valor;
    unsigned int size = numB2.size();
    for(unsigned int x = 0;x < mantisa.size() * 8;x += 8){
        valor = 0;
        unsigned int pos;
        for(pos = 0;pos < 8 && (pos + x) < size;pos++){
            valor = valor << 1;
            valor += (numB2[x + pos] - '0');
        }
        if(x + pos > size){
            valor <<= (8 - pos);
        }
        mantisa[x / 8] = valor;
    }

    normalizar();
}

/** constructor que recibe como parametro un string con el float en base 10*/
Float::Float(const string &num) {
    init(num);
}

/** constructor que recibe un float de la maquina y lo hace de tupo Float */
Float::Float(const float& f) {
    stringstream ss;
    ss << fixed << f;
    string s = ss.str();
    init(s);
}

void Float::setLimit(int i){
    limit = i;
}

int Float::getLimit() {
    return limit;
}

/***********************************************************************/

bool Float::operator==(const Float& other) const {
	if(this->signo != other.signo
			|| this->exponente != other.exponente
			|| !VectorUtils::equals(this->mantisa, other.mantisa)) {
		return false;
	}
	return true;
}

bool Float::operator!=(const Float& f) const {
    return !(*this == f);
}

bool Float::operator<(const Float& other) const {
    bool ret;
    Float cero;
    if(other == cero){
        return this->signo;
    }
    if (*this == cero) {
        return !other.signo;
    }
    if (this->signo == other.signo) {
    	if (this->exponente == other.exponente) {
    		// exponentes iguales
    		vector<byte>::const_iterator it1 = this->mantisa.begin();
    		vector<byte>::const_iterator it2 = other.mantisa.begin();
    		bool termino = false;
    		bool igual = true;
    		while(it1 != this->mantisa.end() && it2 != other.mantisa.end() && !termino) {
    			if(unsh(*it1) < unsh(*it2)) {
    				ret = true;
    				igual = false;
    				termino = true;
    			} else if (unsh(*it1) > unsh(*it2)) {
    				ret = false;
    				igual = false;
    				termino = true;
    			}
                it1++;
                it2++;
    		}
    		//en caso que sean iguales en todos los numeros en comun
    		if (igual) {
    			ret = (this->mantisa.size() < other.mantisa.size());
    		}
    		if (this->signo && !igual) {
    			ret = !ret;
    		}
    	} else {
    		ret = (this->signo && this->exponente > other.exponente);
            ret = ret || (!this->signo && this->exponente < other.exponente);
        }
    } else {
        ret = this->signo;
    }
    return ret;
}

bool Float::operator<=(const Float& f) const {
    return (*this < f) || (*this == f);
}

bool Float::operator>(const Float& f) const {
    return !(*this <= f);
}

bool Float::operator>=(const Float& f) const {
    return !(*this < f);
}

bool Float::similar(const Float& other) const {
    Float temp = *this - other;
    if(temp.modulo() <= Float("0.00000001")){
        return true;
    }
    return false;
}

/**********************************
 * operadores aritmeticos
 **********************************/
Float Float::operator+(const Float& other) const {
    Float f(*this);
    f += other;
    return f;
}

Float Float::operator-(const Float& other) const {
    // a - b = -(-a + b)
    Float menosOther(other);
    if(menosOther != Float()){
        menosOther.signo = !menosOther.signo;
    }
    return *this + menosOther;

}

Float Float::operator*(const Float& other) const {
    Float f(*this);
    f *= other;
    return f;
}

Float Float::operator>>(const unsigned int& n) {
    Float f(*this);
    f >>= n;
    return f;
}

/****************************************
 * operadores de asignacion y aritmeticos
 *****************************************/
void Float::operator+=(const Float& f) {
    if(f == Float()){
        return;
    }
    if(*this == Float()){
        *this = f;
        return;
    }
	unsigned int inicio = abs(f.exponente - this->exponente);
	unsigned int size = inicio + max(this->mantisa.size(), f.mantisa.size());
    if (this->signo == f.signo) {
		assert(size >= f.mantisa.size() && size >= this->mantisa.size());
		vector<byte> result(size, 0);;
		vector<byte> numerochico(size, 0);
		if(this->exponente < f.exponente) {
			VectorUtils::reemplazar(result, 0, f.mantisa);
			VectorUtils::reemplazar(numerochico, inicio, this->mantisa);
			this->exponente = f.exponente;
		} else {
			VectorUtils::reemplazar(result, 0, this->mantisa);
			VectorUtils::reemplazar(numerochico, inicio, f.mantisa);
		}
		assert(numerochico.size() == result.size());
		VectorUtils::sumaDirecta(result, numerochico);
        //si hubo carry
        if(result.size() > numerochico.size()) {
            exponente++;
        }
		this->mantisa = result;
        //por como se hace la suma no deberia haber ceros al principio.
        if(!this->mantisa.empty()){
            assert(this->mantisa[0] != 0);
        }
    } else {// distinto signo.
    	//me quedo con el signo del mas grande
    	this->signo = (f.modulo() < this->modulo()) ? this->signo : f.signo;
    	vector<byte> minuendo(size, 0);
		vector<byte> sustraendo(size, 0);
    	if(this->signo != f.signo){
			//si this era el mas grande
    		VectorUtils::reemplazar(minuendo, 0, this->mantisa);
    		VectorUtils::reemplazar(sustraendo, abs(f.exponente - this->exponente), f.mantisa);
    	} else {
    		VectorUtils::reemplazar(minuendo, 0, f.mantisa);
			VectorUtils::reemplazar(sustraendo, abs(f.exponente - this->exponente), this->mantisa);
    	}
    	VectorUtils::restaDirecta(minuendo, sustraendo);
    	this->mantisa = minuendo;
    	//si this era el mas grande, se queda con su exponente, sino pasa a tener el otro.
    	this->exponente = (this->signo != f.signo) ? this->exponente : f.exponente;
    }
    normalizar();
}

void Float::operator -=(const Float& f){
    // this - f == - (f  + (- this))
    signo = !signo;
    *this += f;
    signo = !signo;
}

void Float::operator*=(const Float& f) {
    Float cero;
    if (*this == cero || f == cero) {
        *this = cero;
        return;
    }
    this->signo = !(this->signo == f.signo);
    this->exponente += f.exponente;
    unsigned int size = max(this->mantisa.size(), f.mantisa.size());
    vector<byte> num1(size, 0);
    vector<byte> num2(size, 0);
    VectorUtils::reemplazar(num1, 0, this->mantisa);
    VectorUtils::reemplazar(num2, 0, f.mantisa);
    this->mantisa = VectorUtils::multiplicacionDirecta(num1, num2);
    normalizar();
}

void Float::operator>>=(unsigned int n) {
    int restaExponente = n / 8;
    int shift = n % 8;
    if (*this == Float()) {
        return;
    }
    exponente -= restaExponente;
    if (shift != 0) {
        byte carry = 0;
        for (int x = 0; x < mantisa.size(); x++) {
            byte temp = (byte)(unsh(mantisa[x]) << (8 - shift));
            mantisa[x] = carry + (byte)(unsh(mantisa[x]) >> shift);
            carry = temp;
        }
        mantisa.push_back(carry);
    }

    normalizar();
}
/************************************************************
 * modulo
 */

Float Float::modulo() const{
	Float f(*this);
	f.signo = false;
	return f;
}


/************************************************************
 **************** NORMALIZAR AL FINAL **********************/

void Float::normalizar() {
	unsigned int restaExponente = VectorUtils::sacarCerosPrincipio(mantisa);
	exponente -= restaExponente;
    VectorUtils::cortarMaxPrecision(mantisa, getLimit());
    VectorUtils::sacarCerosFinal(mantisa);
	if(this->mantisa.empty()){
        exponente = 0;
		this->signo = false;
	}
}

/***********************************************************
 ************* STREAMS ************************************/


string Float::mostrar() const {
    stringstream ss;
    ss << exponente;
    return ((signo)? string("-") : string("")) + "0,"
            + VectorUtils::show(mantisa) + "xb^" + ss.str();
}

ostream & operator<<(ostream& out, const Float& f) {
    out << f.mostrar();
    return out;
}
